//
//  ChangeInfo.swift
//  GerritHermes
//
//  Created by J.Zhou on 2020/1/1.
//  Copyright © 2020 J.Zhou. All rights reserved.
//

import Foundation

// MARK: - ChangeInfo
struct ChangeInfo: Codable {
    enum SubmitType: String, Codable {
        case inherit = "INHERIT"
        case mergeIfNecessary = "MERGE_IF_NECESSARY"
        case fastForwardOnly = "FAST_FORWARD_ONLY"
        case rebaseIfNecessary = "REBASE_IF_NECESSARY"
        case rebaseAlways = "REBASE_ALWAYS"
        case mergeAlways = "MERGE_ALWAYS"
        case cherryPick = "CHERRY_PICK"
    }

    enum Status: String, Codable {
        case new = "NEW"
        case merged = "MERGED"
        case abandoned = "ABANDONED"
    }

    let id: String
    let project: String
    let branch: String
    let topic: String?
    let assignee: AccountInfo?
    let changeID: String
    let subject: String
    let status: Status
    let created: Date
    let updated: Date
    let submitted: Date?
    let submitter: AccountInfo?
    let reviewed: Bool?
    let submitType: SubmitType?
    let mergeable: Bool?
    let submittable: Bool?
    let insertions: Int
    let deletions: Int
    let totalCommentCount: Int?
    let unresolvedCommentCount: Int?
    let hasReviewStarted: Bool
    let number: Int
    let owner: AccountInfo
    let labels: LabelInfo?
    let permittedLabels: PermittedLabels?
    let removableReviewers: [AccountInfo]?
    let reviewers: Reviewers?
    let reviewerUpdates: [ReviewerUpdateInfo]?
    let messages: [ChangeMessageInfo]?
    let requirements: [Requirement]?
    let workInProgress: Bool?

    var currentRevision: String { _currentRevision ?? "current" }
    let _currentRevision: String?

    enum CodingKeys: String, CodingKey {
        case id
        case project
        case branch
        case topic
        case assignee
        case changeID = "change_id"
        case subject
        case status
        case created
        case updated
        case submitted
        case submitter
        case reviewed
        case submitType = "submit_type"
        case mergeable
        case submittable
        case insertions
        case deletions
        case totalCommentCount = "total_comment_count"
        case unresolvedCommentCount = "unresolved_comment_count"
        case hasReviewStarted = "has_review_started"
        case number = "_number"
        case owner
        case labels
        case permittedLabels = "permitted_labels"
        case removableReviewers = "removable_reviewers"
        case reviewers
        case reviewerUpdates = "reviewer_updates"
        case messages
        case requirements
        case _currentRevision = "current_revision"
        case workInProgress = "work_in_progress"
    }
}

// MARK: - Labels
struct LabelInfo: Codable {
    let codeReview: CodeReview

    enum CodingKeys: String, CodingKey {
        case codeReview = "Code-Review"
    }
}

// MARK: - CodeReview
struct CodeReview: Codable {
    // sets by LABELS
    let rejected: AccountInfo?      // -2
    let disliked: AccountInfo?      // -1
    let recommended: AccountInfo?   // +1
    let approved: AccountInfo?      // +2
    let blocking: Bool?
    let value: Int?
    let defaultValue: Int?
    // sets by DETAILED_LABELS
    let all: [ApprovalInfo]?
    let values = VoteValue.allCases
    let _values: [String: String]

    private enum CodingKeys: String, CodingKey {
        case rejected
        case disliked
        case recommended
        case approved
        case blocking
        case value
        case defaultValue = "default_value"
        case all
        case _values = "values"
    }

    func voteValueDescription(_ value: VoteValue) -> String? {
        return _values[value.rawValue]
    }
}

// MARK: - PermittedLabels
struct PermittedLabels: Codable {
    let codeReview: [VoteValue]?

    enum CodingKeys: String, CodingKey {
        case codeReview = "Code-Review"
    }
}

// MARK: - Reviewers
struct Reviewers: Codable {
    let reviewer: [AccountInfo]?
    let cc: [AccountInfo]?
    let removed: [AccountInfo]?

    enum CodingKeys: String, CodingKey {
        case reviewer = "REVIEWER"  // Users with at least one non-zero vote on the change.
        case cc = "CC"              // Users that were added to the change, but have not voted.
        case removed = "REMOVED"    // Users that were previously reviewers on the change, but have been removed.
    }
}

struct ApprovalInfo: Codable {
    // Same as AccountInfo
    let accountID: Int
    let name: String?
    let email: String?
    let username: String?
    let avatars: [AvatarInfo]?
    let status: String?
    var inactive: Bool { _inactive ?? false }
    private let _inactive: Bool?

    // Additional Fields
    let value: Int?
    let permittedVotingRange: VotingRangeInfo?
    let date: Date?

    private enum CodingKeys: String, CodingKey {
        case accountID = "_account_id"
        case name
        case email
        case username
        case avatars
        case status
        case _inactive = "inactive"
        case value
        case permittedVotingRange = "permitted_voting_range"
        case date
    }
}

struct VotingRangeInfo: Codable {
    let min: Int
    let max: Int
}
